package com.gitee.feizns.dynamic.reflect;

import com.gitee.feizns.dynamic.convert.Converts;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * <b>字段操作工具类</b>
 * @author feizns
 * @since 2019/5/12 0012
 */
public abstract class Fields {

    /**
     * 在类型的继承链中查找属性
     * @param name 字段名称
     * @param type 类型
     * @return 获取对应名的字段，如果结果为null 则表示找不到该字段
     */
    public static Field of(Class<?> type, String name) {
        NoSuchFieldException e = null;
        while ( type != null ) {
            try {
                return Reflects.accessible(type.getDeclaredField(name));
            } catch (NoSuchFieldException ex) {
                type = type.getSuperclass();
                e = ex;
            }
        }
        throw new RuntimeException(e);
    }

    /**
     * 获取属性
     * @param type {@link Class}
     * @param filter 过滤规则
     * @return 类型继承链上所有的字段列表
     */
    public static List<Field> of(Class<?> type, Predicate<Field> filter) {
        return of(type).stream().filter(filter).collect(Collectors.toList());
    }

    /**
     * <b>获取一个类继承链上的所有的字段</b>
     * @param type {@link Class}
     * @return 获取一个类上的所有字段
     */
    public static List<Field> of(Class<?> type) {
        List<Field> fields = new ArrayList<>();
        while ( type != null ) {
            fields.addAll(Arrays.asList(type.getDeclaredFields()));
            type = type.getSuperclass();
        }
        return fields;
    }

    /**
     * <b>获取字段的值</b>
     * @param obj 原对象
     * @param field 字段类型
     * @return 返回字段值
     */
    public static Object data(Object obj, Field field) {
        try {
            return Reflects.accessible(field).get(obj);
        } catch (IllegalAccessException e) {
            return null;
        }
    }

    /**
     * <b>获取指定字段名称的值</b>
     * @param obj 对象
     * @param name 字段名称
     * @return 字段值
     */
    public static Object data(Object obj, String name) {
        return data(obj, of(obj.getClass(), name));
    }

    /**
     * <b>获取字段的值（会尝试将字段值转换为对应的类型）</b>
     * @param obj 对象
     * @param field 属性名称
     * @param resultType 期望得到的值类型
     * @return 返回字段的值
     */
    @SuppressWarnings("unchecked")
    public static <T> T data(Object obj, Field field, Class<T> resultType) {
        Object data = data(obj, field);
        return data != null && resultType.isAssignableFrom(data.getClass())
                ? (T) data
                : Converts.to(data, resultType);
    }

    /**
     * <b>获取字段的值（会尝试将字段值转换为对应的类型）</b>
     * @param obj 对象
     * @param name 属性名称
     * @param resultType 期望得到的值类型
     * @return 返回字段的值
     */
    public static <T> T data(Object obj, String name, Class<T> resultType) {
        return data(obj, of(obj.getClass(), name), resultType);
    }

    /**
     * <b>获取field的身上的泛型</b>
     * <p>
     * 例：如有字段的属性类型为 List<String> -> 则返回 [String.class]<br/>
     * 例：如有字段的属性类型为 List{@literal <String>} -> 则返回 [String.class]
     * </p>
     * @param field 田
     * @return 返回字段类型中的泛型类型列表
     */
    public static Class<?>[] ofGenericTypes(Field field) {
        return Classes.ofGenericTypes(field.getGenericType());
    }

}
